﻿using System;
using System.ComponentModel;
using System.IO;
using System.IO.Compression;
using System.Net;

namespace dk.Helper.Base.Core
{
    public class WorldFileDownloader : IWorldFileDownloader
    {
        /// <summary>
        /// Server address
        /// </summary>
        /// <example>divoke-kmene.sk</example>
        private readonly string serverAddress;

        /// <summary>
        /// Web client for downloading files.
        /// </summary>
        private readonly WebClient webClient;

        /// <summary>
        /// Name of the world.
        /// </summary>
        /// <example>sk15</example>
        /// <example>en45</example>
        private readonly string worldName;

        private string gzFilename;

        /// <summary>
        /// Determinates whether download is active or not.
        /// </summary>
        private bool isDownloading;

        private string worldDirectory;

        /// <summary>
        /// Initializes a new instance of the <see cref="T:System.Object"/> class.
        /// </summary>
        public WorldFileDownloader(string worldName, string serverAddress)
        {
            this.worldName = worldName;
            this.serverAddress = serverAddress;
            webClient = new WebClient();
            webClient.DownloadProgressChanged += WebClient_DownloadProgressChanged;
            webClient.DownloadFileCompleted += WebClient_DownloadFileCompleted;
        }

        #region IWorldFileDownloader Members

        /// <summary>
        /// Downloads the file.
        /// </summary>
        /// <param name="filename">The filename.
        /// <example>village.txt</example>
        /// <example>player.txt</example>
        /// <example>ally.txt</example>
        /// </param>
        public void DownloadFile(string filename)
        {
            CreateWorldDirectory();
            if (isDownloading) return;
            isDownloading = true;
            gzFilename = filename + ".gz";

            webClient.DownloadFileAsync(
                new Uri("http://" + worldName + "." + serverAddress + "/map/" + gzFilename),
                Path.Combine(worldDirectory, gzFilename));
        }

        /// <summary>
        /// Occurs when download progress is changed.
        /// </summary>
        public event ProgressEventHandler DownloadProgressChanged;

        /// <summary>
        /// Occurs when download is finished.
        /// </summary>
        public event EventHandler DownloadFinished;

        #endregion

        /// <summary>
        /// Creates the world directory.
        /// </summary>
        private void CreateWorldDirectory()
        {
            string dkHelperDirectory = Path.Combine(
                Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "dk.Helper");
            if (!Directory.Exists(dkHelperDirectory))
                Directory.CreateDirectory(dkHelperDirectory);
            worldDirectory = Path.Combine(dkHelperDirectory, worldName);
            if (!Directory.Exists(worldDirectory))
                Directory.CreateDirectory(worldDirectory);
        }

        /// <summary>
        /// Handles the DownloadProgressChanged event of the WebClient control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Net.DownloadProgressChangedEventArgs"/> instance containing the event data.</param>
        private void WebClient_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
        {
            OnProgressChanged(new ProgressEventArgs(e.ProgressPercentage));
        }

        /// <summary>
        /// Raises the DownloadProgressChanged event.
        /// </summary>
        /// <param name="e">The <see cref="dk.Helper.Base.ProgressEventArgs"/> instance containing the event data.</param>
        private void OnProgressChanged(ProgressEventArgs e)
        {
            if (DownloadProgressChanged != null) DownloadProgressChanged(this, e);
        }

        /// <summary>
        /// Handles the DownloadFileCompleted event of the WebClient control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.ComponentModel.AsyncCompletedEventArgs"/> instance containing the event data.</param>
        private void WebClient_DownloadFileCompleted(object sender, AsyncCompletedEventArgs e)
        {
            DecompressFile();
            isDownloading = false;
            OnDownloadFinished(new EventArgs());
        }

        /// <summary>
        /// Decompresses the file.
        /// </summary>
        private void DecompressFile()
        {
            var fileInfo = new FileInfo(Path.Combine(worldDirectory, gzFilename));

            using (FileStream inFile = fileInfo.OpenRead())
            {
                // Get original file extension, for example
                // "doc" from report.doc.gz.
                string curFile = fileInfo.FullName;
                string origName = curFile.Remove(curFile.Length - fileInfo.Extension.Length);

                //Create the decompressed file.
                using (FileStream outFile = File.Create(origName))
                {
                    using (var Decompress = new GZipStream(inFile, CompressionMode.Decompress))
                    {
                        // Copy the decompression stream 
                        // into the output file.
                        Decompress.CopyTo(outFile);
                    }
                }
            }
        }

        /// <summary>
        /// Raises the DownloadFinished event.
        /// </summary>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        public void OnDownloadFinished(EventArgs e)
        {
            if (DownloadFinished != null) DownloadFinished(this, e);
        }
    }
}